package wiEngine;

import java.util.ArrayList;

import wiBase.WiException;
import wiBase.WiObject;
import wiBase.WiScheduler;
import wiBase.baseInterface.OnDrawerListener;
import wiBase.baseInterface.OnEventSchedulerListener;
import wiBase.baseInterface.OnOutputStreamListener;
import wiBase.baseObject.Event;
import wiBase.baseObject.Node;
import wiBase.baseObject.Packet;
import wiBase.baseObject.WiNetwork;
import wiBase.baseUtil.Position;
import wiModel.WiModelAdapter;

public class WiEngScheduler extends WiScheduler {

    private long _timePeriod = 10;
    boolean _traceAll = true;
    private WiModelAdapter _adapter = null;
    private static WiEngScheduler _scheduler = null;
    private static WiSchedulerThreadPoolExecutor _stpeMain = null;
    private ArrayList<OnDrawerListener> _drawListeners = null;
    private WiNetwork _network0 = null;
    // For analysis network!
    private ArrayList<WiNetwork> _lstNetwork = null;

    public WiEngScheduler() {
	initial();
    }

    private void initial() {
	_adapter = new WiModelAdapter();
	_stpeMain = new WiSchedulerThreadPoolExecutor();

	_network0 = new WiNetwork(0);
	_lstNetwork = new ArrayList<>();

	_drawListeners = new ArrayList<>();
    }

    public static WiEngScheduler getInstance() {
	if (_scheduler == null) {
	    _scheduler = new WiEngScheduler();
	}
	return _scheduler;
    }

    public WiModelAdapter getAdapter() {
	return _adapter;
    }

    /*	
     private ArrayList<WiObject> getListTraceObject(Class<? extends WiObject> clz) throws WiException{
     if(clz.equals(Node.class)) return this._lstTraceNodeObject;
     else if(clz.equals(Packet.class)) return this._lstTracePacketObject;
     else if(clz.equals(Event.class)) return this._lstTraceEventObject;
     else throw new WiException(WiException.TYPE_DEFAULT, "No class respect");
     }
     private ArrayList<WiObject> getListTraceObject(WiObject object) throws WiException {
     if(object instanceof Node) return this._lstTraceNodeObject;
     else if(object instanceof Packet) return this._lstTracePacketObject;
     else if(object instanceof Event) return this._lstTraceEventObject;
     else throw new WiException(WiException.TYPE_DEFAULT, "No class respect");
     }
     private ArrayList<WiObject> getListTraceObject(Class<? extends WiObject> clz,WiNetwork network) throws WiException{
     if(clz.equals(Node.class)) return network.getLstNodes();
     else if(clz.equals(Packet.class)) return network.getLstPackets();
     else if(clz.equals(Event.class)) return network.getLstEvents();
     else throw new WiException(WiException.TYPE_DEFAULT, "No class respect");
     }
     */
    public void setTraceFile(String traceFile) {
	_adapter.setTraceFile(traceFile);
    }

    public void parse() throws WiException {
	if (_adapter == null) {
	    throw new WiException(WiException.TYPE_NULL, "Null Adapter");
	}
	_adapter.parse();

	analysis(_timePeriod);
    }

    public void analysis(long timePeriod) throws WiException {

	for (WiObject object : _adapter.getListEvents()) {
	    Event event = (Event) object;
	    switch (event.type) {
		case "send":
		    // not broadcast
		    WiNetwork network = getOrCreateNetwork(event.getTime(), timePeriod);

		    break;
		case "forward":
		    break;
		case "receive":
		    break;
		case "drop":
		    break;
		case "move":
		    break;
		case "sleep":
		    break;
		case "wake":
		    break;

		default:
		    break;
	    }
	}
    }

    public WiNetwork getOrCreateNetwork(long crtTime, long period) {
	if (_lstNetwork == null) {
	    _lstNetwork = new ArrayList<>();
	}

	WiNetwork lastNetwork = _lstNetwork.get(_lstNetwork.size() - 1);
	if (lastNetwork.get_time() > crtTime) {
	    return lastNetwork;
	}

	WiNetwork newNetwork = new WiNetwork(lastNetwork.get_time() + period);
	_lstNetwork.add(newNetwork);
	return newNetwork;
    }

    public void addTraceNode(int idNode, OnOutputStreamListener listener) throws WiException {
	_traceAll = false;
	for (WiObject _node_ : _adapter.getListNodes()) {
	    if (((Node) _node_).id == idNode) {
		_node_.setOutputListener(listener);
		break;
	    }
	}
    }

    public void addTracePacket(int idPacket, OnOutputStreamListener listener) throws WiException {
	_traceAll = false;
	for (WiObject _packet_ : _adapter.getListPackets()) {
	    if (((Packet) _packet_).id.equals(idPacket + "")) {
		_packet_.setOutputListener(listener);
		break;
	    }
	}
    }

    public void addTraceEvent(String type, OnOutputStreamListener listener) throws WiException {
	_traceAll = false;
	for (WiObject _event_ : _adapter.getListEvents()) {
	    if (((Event) _event_).type.equals(type + "")) {
		_event_.setOutputListener(listener);
	    }
	}
    }

    public void setTraceAll() throws WiException {
	_traceAll = true;
    }

    public void schedule() {
	schedule(-1, -1);
    }

    public void schedule(long startTime, long finishTime) {
	WiNetwork _network_ = getNetwork(startTime, finishTime);
	schedule(_network_);
    }
    // Faster!!!

    public void schedule(WiNetwork network) {
	WiNetwork _network_ = network;
	System.out.println("start Scheduler:" + _adapter.getFileTrace() + " start at:" + _network_.get_time());
	long time1 = System.currentTimeMillis();
	for (WiObject object : _adapter.getListEvents()) {
	    Event event = (Event) object;

	    if (!_traceAll) {
		event.setTraceSchedulerListener(new EventTraceListener(this));
	    } else {
		event.setTraceSchedulerListener(new OnEventSchedulerListener() {
		    public void write(WiObject data) {
//						System.out.println(data);
			if (_drawListeners != null) {
			    for (OnDrawerListener listener : _drawListeners) {
				listener.updateState((Event) data);
			    }
			}
		    }
		});
	    }
//			event.run();
//			_stpeMain.schedule(event, ((long) Double.parseDouble(((Event)event).time)-_network_.get_time()),
//					TimeUnit.SECONDS);
	}
	long time2 = System.currentTimeMillis();
	//System.out.println(time2 - time1);
	// for safe!
	new Thread(new Runnable() {
	    public void run() {
		while (true) {
		    if (_stpeMain.getCompletedTaskCount() == _stpeMain
			    .getTaskCount()) {
			_stpeMain.shutdown();
			break;
		    }
		}
		try {
		    Thread.sleep(1000);
		} catch (InterruptedException e) {
		    e.printStackTrace();
		}
	    }
	}).start();
    }

    public WiNetwork getNetwork(long startTime, long finishTime) {
//		if(startTime<0) startTime=0;
//		if(startTime<0 && finishTime<0) return _network0;
//		
//		boolean toEnd=false;
//		if(finishTime<=0) toEnd=true;
//		
	WiNetwork _network_ = new WiNetwork(startTime);
//		_network_.set_time(startTime);
//		
//		for(WiObject _node_:_adapter.getListNodes()){
//			_network_.getLstNodes().add(_node_);
//		}
//		for(WiObject _packet_:_adapter.getListPackets()){
//			_network_.getLstPackets().add(_packet_);
//		}
//		for(WiObject _event_:_adapter.getListEvents()){
//			long _evTime_=(long)Double.parseDouble(((Event)_event_).time);
//			if(!toEnd){
//				if(_evTime_>finishTime){
//					break;
//				}
//			}
//			if(_evTime_>startTime){
//				_network_.getLstEvents().add(_event_);
//			}
//		}
	return _network_;
    }

    public Node getNode(int idNode) {
	for (WiObject _object_ : _adapter.getListNodes()) {
	    if (((Node) _object_).id == idNode) {
		return (Node) _object_;
	    }
	}
	return null;
    }

    public Packet getPacket(int idPacket) {
	for (WiObject _object_ : _adapter.getListPackets()) {
	    if (((Packet) _object_).id.equals(idPacket + "")) {
		return (Packet) _object_;
	    }
	}
	return null;
    }

    public Position getMaxNode() {
	Position p = new Position(0, 0);
	double maxWidth = 0;
	double maxHeight = 0;
	for (WiObject object : _adapter.getListNodes()) {
	    Node node = (Node) object;
	    if (node.position.getX() > maxWidth) {
		maxWidth = node.position.getX();
	    }
	    if (node.position.getY() > maxHeight) {
		maxHeight = node.position.getY();
	    }
	}
	p.setX(maxWidth);
	p.setY(maxHeight);

	return p;
    }

    public void historyNode(int idNode) {
    }
    /*
	
     public void schedule() {
     _startTime=-1;
     _endTime=-1;
     schedule(_startTime, _endTime);
     }
	
     public void remove(WiObject objectToRemove) throws WiException {
     getListTraceObject(objectToRemove).remove(objectToRemove);
     }

	
     public void remove(Class<? extends WiObject> clsToRemove) {
     int typeCls=-1;
     if(clsToRemove == Node.class) typeCls =0;
     else if(clsToRemove == Packet.class) typeCls=1;
     else if(clsToRemove == Event.class) typeCls=2;
		
     switch (typeCls) {
     case 0:
     _lstTraceNodeObject=new ArrayList<>();
     break;
     case 1:
     _lstTracePacketObject=new ArrayList<>();
     break;
     case 2:
     _lstTraceEventObject=new ArrayList<>();
     break;
     default:
     break;
     }
		
     }
	
	
     public void setConfFile(String cfgFile) {
     this._adapter.setConfFile(cfgFile);
     }
	
	
     public void setBeginSched(long startTime, TimeUnit timeUnit) {
     this._startTime=startTime;
     }

	
     public void addTraceWiObject(Class<? extends WiObject> typeTraceObject) {
     if(typeTraceObject.equals(Node.class)){
     this._lstTraceNodeObject=new ArrayList<>();
     }else if(typeTraceObject.equals(Packet.class)){
     this._lstTracePacketObject=new ArrayList<>();
     }else if(typeTraceObject.equals(Event.class)){
     this._lstTraceEventObject=new ArrayList<>();
     }
     }

	
     public void addTraceWiObject(WiObject object) {
     this._lstTraceEventObject.add(object);
     }

	
     public long getMilliTimeParse() {
     return _adapter.getMilliTimeParse();
     }

	
     public ArrayList<WiObject> getStateNetwork(Class<? extends WiObject> clz,long inTime) throws WiException {
     return getListTraceObject(clz);
     }

	
     public void setEndSched(long endTime, TimeUnit timeUnit) {
     this._endTime=endTime;
     }

	
     public void setWakeupListener(WiObject object,OnOutputStreamListener listener) {
     object.setOutputListener(listener);
     }
	
	
     public void setWakeupListener(Class<? extends WiObject> clz,
     OnOutputStreamListener listener) throws WiException {
     for(WiObject object:getListTraceObject(clz,_network0)){
     object.setOutputListener(listener);
     }
     }
	
     */

    public void run() {
	schedule();
    }

    public void addOnDrawerListener(OnDrawerListener listener) {
	_drawListeners.add(listener);
    }

    public ArrayList<WiObject> getListNodes() {
	// TODO Auto-generated method stub
	return null;
    }

    public ArrayList<WiObject> getListPackets() {
	// TODO Auto-generated method stub
	return null;
    }

    public ArrayList<WiObject> getListAllEvents(long startTime, long endTime) {
	// TODO Auto-generated method stub
	return null;
    }

    public ArrayList<Event> tracePacket(int idPacket) {
	// TODO Auto-generated method stub
	return null;
    }

    public void setRegion(Integer... idNodes) {
	// TODO Auto-generated method stub
    }
}
